package org.framework.common.api.support.model;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.apache.el.util.ReflectionUtil;
import org.framework.common.api.query.Criterion.Operator;
import org.framework.common.api.query.Criterion.ValueType;
import org.framework.common.api.query.GroupBean;
import org.framework.common.api.query.OrderBean;
import org.framework.common.api.query.QueryBean;
import org.framework.common.ext.exception.CommonException;
import org.framework.common.ext.util.DateUtil.DatePattern;
import org.framework.common.ext.util.JsonUtil;
import org.framework.common.ext.util.StringUtil;
import org.springframework.ui.ModelMap;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.TextNode;

@JsonAutoDetect
public class QueryModel implements Serializable {

	/** serialVersionUID(long):. */
	private static final long serialVersionUID = -8580808826914296664L;
	
	private static final String KEY_FIELD_NAME 		= "fieldName";
	private static final String KEY_FIELD_TYPE 		= "fieldType";
	private static final String KEY_FIELD_VALUE 	= "fieldValue";
	private static final String KEY_FIELD_VALUES 	= "fieldValues";
	private static final String KEY_FIELD_OPERATOR 	= "operator";
	
	private static final String KEY_ORDER_ORDER = "order";
	private static final String KEY_ORDER_SORT 	= "sort";
	private static final String KEY_ORDER_DESC 	= "desc";
	private static final String KEY_ORDER_ASC 	= "asc";
	
	private static final String KEY_ORDER_GROUP = "group";

	/* 实体类
	 * eg: ajax data : {"entityClass":"com.app.miwawa.api.esm.entity.Adviser"}
	 */
	private String entityClass;
	
	/* 列表模糊搜索
	 * eg:
	 * 	@Search 
	 * 	@Column(...)
	 * 	private String name;
	 */
	private String search;
	
	/* 分页大小
	 * 每页显示的条目数
	 */
	private Integer limit = 10;
	
	/* 当前分页开始条目序号
	 * offset = current page * limit - limit
	 */
	private Integer offset = 0;
	
	/* 排序集合
	 * eg:  ajax data : { "order":[{"sort":"invalid","order":"desc"}] }
	 */
	private List<ModelMap> order;
	
	/* 分组集合
	 * eg:  ajax data : { "group":[{"filedName":"articleId"}] }
	 */
	private List<ModelMap> group;
	
	/* 关系为and的条件集合
	 * eg:  ajax data : { "and":[{"operator":"EQ", "fieldName":"invalid", "fieldValue":"F"}] }
	 * eg:  ajax data : { "and":[{"operator":"BETWEEN", "fieldName":"createDate", "fieldType":"DATES || DATETIMES", "fieldValue":["yyyy-MM-dd HH:mm:ss","yyyy-MM-dd HH:mm:ss"]}] }
	 */
	private List<ModelMap> and;
	
	/* 关系为or的条件集合
	 * eg:  ajax data : { "or":[{"operator":"EQ", "fieldName":"invalid", "fieldType":"java.lang.String", "fieldValue":"F"}] }
	 */
	private List<ModelMap> or;
	
	public QueryModel() {

	}
	
	public QueryModel(String entityClass) {
		this.entityClass = entityClass;
	}
	
	public QueryModel(Class<?> entityClass) {
		this.entityClass = entityClass.getName();
	}
	
	public QueryModel(String entityClass, Integer limit, Integer offset) {
		this.limit = limit;
		this.offset = offset;
		this.entityClass = entityClass;
	}
	
	public QueryModel(Class<?> entityClass, Integer limit, Integer offset) {
		this.limit = limit;
		this.offset = offset;
		this.entityClass = entityClass.getName();
	}
	
	public QueryModel(Class<?> entityClass, String limit, String offset) {
		this.limit = Integer.valueOf(limit);
		this.offset = Integer.valueOf(offset);
		this.entityClass = entityClass.getName();
	}
	
	/**
	 * 返回实体类.
	 * @return
	 * @author chanlong(陈龙)
	 * @date 2016年1月20日 下午3:49:39
	 */
	@SuppressWarnings("unchecked")
	public <T> Class<T> getClass4Name() {
		try {
			return (Class<T>) ReflectionUtil.forName(entityClass);
		} catch (ClassNotFoundException e) {
			new CommonException();
		}
		return null;
	}

	/**
	 * 返回And集合中的QueryBean数组.
	 * @return
	 * @author chanlong(陈龙)
	 * @date 2016年1月19日 下午5:34:05
	 */
	public QueryBean[] getQueryBeans4And(){
		if(and != null){
			String json = JsonUtil.serialize(and, DatePattern.DATETIME);
			return JsonUtil.getBean(json, QueryBean[].class, QuerybeanModule.module());
		}
		return null;
	}
	
	/**
	 * 返回Or集合中的QueryBean数组.
	 * @return
	 * @author chanlong(陈龙)
	 * @date 2016年1月19日 下午5:34:50
	 */
	public QueryBean[] getQueryBeans4Or(){
		if(or != null){
			String json = JsonUtil.serialize(or, DatePattern.DATETIME);
			return JsonUtil.getBean(json, QueryBean[].class, QuerybeanModule.module());
		}
		return null;
	}
	
	/**
	 * 返回Order集合中的OrderBean数组.
	 * @return
	 * @author chanlong(陈龙)
	 * @date 2016年1月19日 下午5:35:14
	 */
	public OrderBean[] getOrderBeans(){
		if(order != null){
			String json = JsonUtil.serialize(order);
			return JsonUtil.getBean(json, OrderBean[].class);
		}
		return null;
	}
	
	public GroupBean[] getGroupBeans(){
		if(group != null){
			String json = JsonUtil.serialize(group);
			return JsonUtil.getBean(json, GroupBean[].class);
		}
		return null;
	}
	
	/** 添加并列条件 */
	public void addAndQuery(final String name, final Object value, final Operator operator){
		if(and == null) {
			and = new ArrayList<ModelMap>();
		}
		and.add(createQuery(name, value, operator));
	}
	
	/** 添加并列条件 */
	public void addAndQuery(final String name, final Object value, final Operator operator, final ValueType type){
		if(and == null) {
			and = new ArrayList<ModelMap>();
		}
		and.add(createQuery(name, value, operator, type));
	}
	
	/** 添加选择条件 */
	public void addOrQuery(final String name, final Object value, final Operator operator){
		if(or == null) {
			or = new ArrayList<ModelMap>();
		}
		or.add(createQuery(name, value, operator));
	}
	
	/** 添加排序规则 */
	public void addOrder(final String sort, final boolean desc){
		if(order == null) {
			order = new ArrayList<ModelMap>();
		}
		order.add(createOrder(sort, desc ? KEY_ORDER_DESC : KEY_ORDER_ASC));
	}
	
	/** 添加分组条件 */
	public void addGroup(final String name){
		if(group == null) {
			group = new ArrayList<ModelMap>();
		}
		group.add(createGroup(name));
	}
	
	public void clearAndQuery(){ if(and != null) and.clear(); }
	
	public void clearOrQuery(){ if(or != null) or.clear(); }

	public void clearOrder(){ if(order != null) order.clear(); }

	private ModelMap createQuery(final String name, final Object value, final Operator operator, final ValueType type) {
		ModelMap map = new ModelMap();
		map.put(KEY_FIELD_TYPE, type);
		return createQueryMap(map, name, value, operator);
	}
	
	private ModelMap createQuery(final String name, final Object value, final Operator operator){
		ModelMap map = new ModelMap();
		return createQueryMap(map, name, value, operator);
	}
	
	private ModelMap createOrder(final String sort, final String order){
		ModelMap map = new ModelMap();
		map.put(KEY_ORDER_SORT, sort);
		map.put(KEY_ORDER_ORDER, order);
		return map;
	}
	
	private ModelMap createGroup(final String group){
		ModelMap map = new ModelMap();
		map.put(KEY_ORDER_GROUP, group);
		return map;
	}
	
	private ModelMap createQueryMap(ModelMap map, final String name, final Object value, final Operator operator){
		map.put(KEY_FIELD_NAME, name);
		if(value.getClass().isArray()){
			map.put(KEY_FIELD_VALUES, value);
		}else{
			map.put(KEY_FIELD_VALUE, value);
		}
		map.put(KEY_FIELD_OPERATOR, operator);
		return map;
	}
	
	/**
	 * 自定义 Jackson 反序列化模块
	 * <p>描述: 当json对象的值为对象数组时触发。页面请求时，为数组的字符串形式</p>
	 * <p>Company: Professional</p>
	 * @author chanlong(陈龙)
	 * @date 2016年2月14日 下午1:25:09
	 * @version 1.0.2016
	 */
	private static class QuerybeanModule extends SimpleModule{
	
		/** serialVersionUID(long):. */
		private static final long serialVersionUID = 4376559297536053759L;
		
		static QuerybeanModule instance;
		
		private QuerybeanModule() {
			this.addDeserializer(Object[].class, new JsonDeserializer<Object[]>() {
				@Override
				public Object[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
					TreeNode root = p.getCodec().readTree(p);
					if(root.isArray()){
						ArrayNode nodes = ((ArrayNode)root);
						Object[] array = new Object[nodes.size()];
						for(int i = 0, size = nodes.size(); i < size; i++){
							JsonNode node = nodes.get(i);
							if(node.isInt()){
								array[i] = node.intValue();
							}else if(node.isDouble()){
								array[i] = node.floatValue();
							}else{
								array[i] = node.textValue();
							}
						}
						return array;
					}else{
						String textvalue = ((TextNode)root).textValue();
						return StringUtil.isNotEmpty(textvalue) ? JsonUtil.getBean(textvalue, String[].class) : null;
					}
				}
			});
		}
		
		private static QuerybeanModule module(){
			if(instance == null) {
				instance = new QuerybeanModule();
			}
			return instance;
		}
	}
	
	public Integer getLimit() {
		return limit;
	}

	public void setLimit(Integer limit) {
		this.limit = limit;
	}

	public Integer getOffset() {
		return offset;
	}

	public void setOffset(Integer offset) {
		this.offset = offset;
	}

	public String getSearch() {
		return search;
	}

	public void setSearch(String search) {
		this.search = search;
	}

	public String getEntityClass() {
		return entityClass;
	}
	
	public void setEntityClass(String entityClass) {
		this.entityClass = entityClass;
	}
	
	public void setEntityClass(Class<?> entityClass) {
		this.entityClass = entityClass.getName();
	}

	public List<ModelMap> getOrder() {
		return order;
	}

	public void setOrder(List<ModelMap> order) {
		this.order = order;
	}

	public List<ModelMap> getAnd() {
		return and;
	}

	public void setAnd(List<ModelMap> and) {
		this.and = and;
	}

	public List<ModelMap> getOr() {
		return or;
	}

	public void setOr(List<ModelMap> or) {
		this.or = or;
	}

	public List<ModelMap> getGroup() {
		return group;
	}

	public void setGroup(List<ModelMap> group) {
		this.group = group;
	}
}
